package com.sheep.jucdemo.part1.part1_1;

import java.util.ArrayList;
import java.util.List;

/**
 * 使用wait和notify模拟生产者和消费者
 *
 * 2022.01.23 16:06:01
 * 字数 214
 * 阅读 32
 * wait()和notify()
 * wait()和notify()的方法并不是线程对象专属的方法，Java中所有的对象都有这两个方法。
 * object.wait(): 持有obj对象锁的当前线程进入等待状态，并释放该对象的锁。
 * object.notify(): 唤醒之前进入等待状态的线程，使这些线程重新尝试获取对象锁。
 *
 * 模拟生产者和消费者模式
 * 仓库，生产者，消费者三者之间，常常达到供需平衡的状态，即：
 * 生产者生产指定量的产品，便不能生产(wait)，需要通知消费者进行消费(notify)；
 * 消费者消费完所有库存，便不能再消费(wait)，需要告知生产者进行生产(notify)。
 * 如下代码模拟以上模式：
 */
public class ConsumerAndProducerTest {
    public static void main(String[] args) {
        // 模拟仓库
        List<Integer> list = new ArrayList<Integer>();
        // 创建生产者线程
        Thread producer = new Thread(new Producer(list), "生产者");
        // 创建消费者线程
        Thread consumer = new Thread(new Consumer(list), "消费者");
        // 同时启动生产者和消费着
        producer.start();
        consumer.start();
    }
}


/**
 * 消费者类
 */
class Consumer implements Runnable {
    final List<Integer> list;
    int number = 0;

    public Consumer(List<Integer> list) {
        this.list = list;
    }

    @Override
    public void run() {
        synchronized (list) {
            while (true) {
                // 判断仓库中商品是否空了
                while (list.size() == 0) {
                    try {
                        list.wait();  // 没有商品，进入等待状态并释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 消费库存中第一件商品，并返回商品编号
                int id = list.remove(0);
                System.out.println(Thread.currentThread().getName() + "消费了商品" + id + String.format("，已消费%d件商品！", ++number));
                // 唤醒其他线程
                list.notifyAll();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}


/**
 * 生产者类
 */
class Producer implements Runnable {
    final List<Integer> list;
    int id = 0;

    public Producer(List<Integer> list) {
        this.list = list;
    }

    @Override
    public void run() {
        synchronized (list) {
            while (true) {
                // 判断仓库中商品是否有指定数量库存
                while (list.size() >= 2) {
                    try {
                        list.wait();  // 有商品，进入等待状态并释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 生产
                list.add(++id);
                System.out.println(Thread.currentThread().getName() + "生产了商品" + id + String.format(", 现仓库有%d件未售出！", list.size()));
                // 唤醒其他线程
                list.notifyAll();
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}